//
//  TabFile.h
//  germ
//
//  Created by Rodger B. Voelker
//  Modification Date: 1/24/05
//  Purpose: provide an API to tab-delimited database
//  
//

#ifndef __germ__TabFile__
#define __germ__TabFile__

// Data field numbering begins at 0.
// Basically each method is an individual handler for the db file whose name it is attached to.  It provides access to the
// data but provides only limited file manipulations: such as appending, clearing, need to add inserting etc.
// More complex manipulations are handled by classes such as SeqDB which manipulate TabFile objects

#include <fstream>
#include <vector>
#include <iostream>
#include <string>
#include <sstream>
#include "Utility.h"

using namespace std;

class TabFile{
  
public:
  
  typedef vector<string> typeStrings;
  typedef vector< typeStrings > typeDBase;
  typedef int tMode;
  static tMode read;
  static tMode write;
  //open, close, clear
  TabFile(void); //create an empty object
  ~TabFile(void);
  bool open( string file_name ); //assume has header
  bool open( string file_name, bool hasHeader );
  bool open( string file_name, tMode mode ); //mode should be TabFile::read or TabFile::write
  bool initialize(void);
  bool initialize(bool hasHeader);
  void reset(void);
  void close(void);
  bool clearDBFile(void); //erase the data!!! use with caution.
  //attributes
  bool eof(void); //return true if EOF
  streampos fp_tell(void); //returns current position of file pointer
  bool fp_seek(streampos); //set stream pointer to pos relative to begining of file
  bool fp_start(void); //save current position and set file pointer to start of file
  bool fp_data_start(void); //set to start to data ie first line after header
  bool fp_record_data_start(bool hasHeader); //set _data_start pos
  unsigned long nRows(void); //number of data rows
  int nFields(void){ return _header.size(); }
  typeStrings getHeader(void); //return the first line, which should be the header, parsed into a vector
  string getHeaderString(void){ return _headerstring; } //return the header as a tab-delimited string
  bool setHeader(vector<string>& fields); //set header to fields contained in vector
  bool writeHeader(void);
  void print(ostream& stream ); //print file to screen
  string returnFileName(void){ return _masterfile_name; }
  bool verifyRange( int field );
  void printHeaderFormatted(ostream& stream, int columns); //print the header to the stream using the number of specified columns
  bool headerPrinted(void){ return _headerprinted; }
  //select a field index using input from user
  int getFieldIndex( string query_message );
  int getFieldIndex( string query_message, int field_from_commandline ); //version that verifies that field_from_commandline is in range and if not gets a new one and decrements field																	//pass -1 for field_from_commandline to indicate that the user has not selected a field from the command line
  //data access: get the entire row simply read as a string, delimiter and all
  unsigned getNextRow( string& line); //assigns next line to string, returns lines read
  unsigned getNextRow( typeStrings& lines, unsigned max_lines); //reads at most specified number of lines, returns lines read
  unsigned getNextRow( typeStrings& lines); //reads entire file (minus header) into vector, returns lines read
  //data access: get just specific fields or the entire row parsed into vectors, only retrieves as string, user is responsible for conversions
  bool getField( typeStrings& data); //Parses next line into vector breaking at tab delimiters and advances file pointer
  bool getField( typeStrings& data, string& buffer); //Parses next line into vector breaking at tab delimiters and advances file pointer and catch the line intact in buffer
  bool getField( typeStrings& data, vector<int>& fields); //Parses next line into vector but only retrieves specified fields (1st field = 0) and advances file pointer
  bool getField( string& data, int field); //Parses next line and retrieves only specified field and advances file pointer
  //data entry
  bool addRow( typeStrings& new_data ); //append data to end of file, add a single line
  bool addRow( typeDBase& new_data ); //append data to end of file, add a bunch of lines
  bool addRow( stringstream& new_data ); //add a single line using tab-delimited stringstream, Caution: there's no checking that delimter is a tab
  
private:
  
  string _masterfile_name;
  fstream _masterFH; //attached to current master file
  ios_base::openmode _masterFH_mode; //current file mode
  int _nfields; //number of fields
  unsigned long _nrows; //number of lines minus header, only determined if needed since it can take some time for large files
  streampos _data_start; //first line after header or start if no header
  streampos _saved_pos; //used to record position during manipulations
  ios_base::openmode _saved_mode;
  streampos _end_last_read; //Note: discontinued //pos for end of last read, used to track lines read allowing for other operations that might reset the file pointer
  string _headerstring;
  typeStrings _header;
  bool _isModified; //flag indicating if file modified since last counting of rows
  bool _headerprinted; //indicates whether the header has been printed yet, used by getFieldIndex
  Utility io;
  //private functions
  unsigned _split_tab(string& instring, vector<string>& outvec);
  void _fp_record(void); //set internal reference, used by other member functions
  void _fp_restore(void); //set file pointer to _saved_pos, used by member functions
  bool _openFileForRead(void);
  bool _openFileForWrite(void);
  bool _openFileForAppend(void);
  bool _canRead(void); //check filestate is OK and openmode = ios::in
  bool _canWrite(void); //check filestate is OK and openmode = ios::out|ios::trunc
  bool _canAppend(void); //check filestate is OK and openmode = ios::out|ios::app
};

#endif /* defined(__germ__TabFile__) */
